package com.snap.vseries.analyze;

import org.apache.commons.math3.analysis.UnivariateFunction;
import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.exception.NullArgumentException;
import org.apache.commons.math3.util.FastMath;

/**
 * 双S函数的标准化，y轴方向偏移h,用来求函数与轴的交点（使用牛顿迭代法）
 */
public class StdDoubleSigmoidWithYOffset implements UnivariateDifferentiableFunction, UnivariateFunction {

    private final double c;
    private final double d;
    private final double e;
    private final double f;
    private final double g;
    private final double h;

    public StdDoubleSigmoidWithYOffset(double... params) {
        if (params == null) {
            throw new NullArgumentException();
        }
        if (params.length != 6) {
            throw new DimensionMismatchException(params.length, 6);
        }
        this.c = params[0];
        this.d = params[1];
        this.e = params[2];
        this.f = params[3];
        this.g = params[4];
        this.h = params[5];
    }

    private static double getValue(double x,
                                   double c,
                                   double d,
                                   double e,
                                   double f,
                                   double g,
                                   double h) {
        return c / ((1 + FastMath.exp(-d * (x - e))) * (1 + FastMath.exp(-f * (x - g)))) - h;
    }

    @Override
    public double value(double x) {
        return StdDoubleSigmoidWithYOffset.getValue(x, c, d, e, f, g, h);
    }

    @Override
    public DerivativeStructure value(DerivativeStructure t) throws DimensionMismatchException {
        return t.subtract(e).multiply(-d).exp().add(1)
                .multiply(t.subtract(g).multiply(-f).exp().add(1))
                .reciprocal()
                .multiply(c)
                .subtract(h);
    }
}